;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2022 unwox <me@unwox.com>
;;;
;;; This file is part of GNU Guix.
;;;
;;; GNU Guix is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 3 of the License, or (at
;;; your option) any later version.
;;;
;;; GNU Guix is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.

(define-module (guixrus home services mksh)
  #:use-module (gnu home services)
  #:use-module (gnu home services shepherd)
  #:use-module (gnu home services utils)
  #:use-module (gnu packages shells)
  #:use-module (gnu services configuration)
  #:use-module (guix gexp)
  #:use-module (guix packages)
  #:use-module (ice-9 match)

  #:export (home-mksh-service-type
            home-mksh-configuration))

(define (serialize-aliases field-name val)
  #~(string-append
     #$@(map
         (match-lambda
          ((key . #f)
           "")
          ((key . #t)
           #~(string-append "alias " #$key "\n"))
          ((key . value)
           #~(string-append "alias " #$key "=\"" #$value "\"\n")))
         val)))

(define (serialize-binds field-name val)
  #~(string-append
     #$@(map
         (match-lambda
          ((key . #f)
           "")
          ((key . #t)
           "")
          ((key . value)
           #~(string-append "bind " #$key "=" #$value "\n")))
         val)))

(define (serialize-options field-name val)
  #~(string-append
     #$@(map (lambda (opt) (string-append "set -o " opt "\n"))
             val)))

(define (serialize-posix-env-vars field-name val)
  (environment-variable-shell-definitions val))

(define (make-env-string-serializer name export?)
  (lambda (field-name val)
    (if ((negate string-null?) val)
        #~(string-append
           (if #$export? "export " "")
           #$name
           "=\""
           #$val
           "\"\n")
        "")))

(define-configuration home-mksh-configuration
  (package
   (package mksh)
   "The mksh package to use.")
  (environment-variables
   (alist '())
   "Association list of environment variables to set for the mksh session.  The
rules for the @code{home-environment-variables-service-type} apply
here (@pxref{Essential Home Services})."
   serialize-posix-env-vars)
  (aliases
   (alist '())
   "Association list of aliases to set for the mksh session.  The aliases will
be defined after the contents of the @code{mkshrc} field has been put in the
@file{.mkshrc} file."
   serialize-aliases)
  (mkshrc
   (text-config '())
   "List of file-like objects, which will be added to @file{.mkshrc}.  Used for
executing user's commands at start of interactive shell (The shell for
interactive usage started by typing @code{mksh} or by terminal app or any other
program).")
  (history-file
   (string "")
   "The path to the file used to store command history."
   (make-env-string-serializer "HISTFILE" #t))
  (history-size
   (string "")
   "The number of commands normally stored for history.  The default
is 2047.  The maximum is 65535."
   (make-env-string-serializer "HISTSIZE" #t))
  (prompt
   (string "")
   "The primary prompt for interactive shells (PS1).  The default prompt is
@code{$ } for non-root users, @code{# } for root.  Since backslashes and other
special characters may be interpreted by the shell, to set prompt either escape
the backslash itself or use double quotes."
   (make-env-string-serializer "PS1" #f))
  (options
   (list '())
   "The list of shell options for changing/extending default behavior of the
mksh shell.  For a full list of options run @code{set -o} command."
   serialize-options)
  (binds
   (alist '())
   "The list of custom key bindings for the mksh shell.  For a full list of
current bindings run @code{bind} command without arguments.  For a list of
available commands run @code{bind -l}."
   serialize-binds))

(define (home-mksh-configuration-file config)
  (define (filter-fields field)
    (filter-configuration-fields home-mksh-configuration-fields
                                 (list field)))

  (define (serialize-field field)
    (serialize-configuration
     config
     (filter-fields field)))

  `((".mkshrc"
    ,(mixed-text-file
      "mkshrc"
      (serialize-field 'options)
      (serialize-field 'mkshrc)
      (serialize-field 'environment-variables)
      (serialize-field 'history-file)
      (serialize-field 'history-size)
      (serialize-field 'prompt)
      (serialize-field 'aliases)
      (serialize-field 'binds)))))

(define (home-mksh-packages config)
  (list (home-mksh-configuration-package config)))

(define home-mksh-service-type
  (service-type
   (name 'home-mksh)
   (extensions
    (list (service-extension
           home-files-service-type
           home-mksh-configuration-file)
          (service-extension
           home-profile-service-type
           home-mksh-packages)))
   (default-value (home-mksh-configuration))
   (description "Install and configure the MirBSD Korn shell.")))
